﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;

namespace Devonline.Core
{
    public static class ValidateExtensions
    {
        #region 检测字符串中是否包含列表中的关键词
        /// <summary>
        /// 检测字符串中是否包含列表中的关键词
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <param name="keys">关键词列表</param>
        /// <param name="ignoreCase">忽略大小写</param>
        /// <returns></returns>
        public static bool Contains(this string value, IEnumerable<string> keys, bool ignoreCase = true)
        {
            if (!keys.Any() || string.IsNullOrEmpty(value))
            {
                return false;
            }

            if (ignoreCase)
            {
                return Regex.IsMatch(value, string.Join("|", keys), RegexOptions.IgnoreCase);
            }

            return Regex.IsMatch(value, string.Join("|", keys));

        }
        #endregion

        #region 匹配Email
        /// <summary>
        /// 匹配Email
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <param name="isMatch">是否匹配成功，若返回true，则会得到一个Match对象，否则为null</param>
        /// <returns>匹配对象</returns>
        public static Match MatchEmail(this string value, out bool isMatch)
        {
            if (string.IsNullOrEmpty(value))
            {
                isMatch = false;
                return null;
            }

            Match match = Regex.Match(value, @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
            isMatch = match.Success;
            return isMatch ? match : null;
        }
        /// <summary>
        /// 匹配Email
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <returns>是否匹配成功</returns>
        public static bool MatchEmail(this string value)
        {
            MatchEmail(value, out bool success);
            return success;
        }
        #endregion

        #region 匹配完整的URL
        /// <summary>
        /// 匹配完整格式的URL
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <param name="isMatch">是否匹配成功，若返回true，则会得到一个Match对象，否则为null</param>
        /// <returns>匹配对象</returns>
        public static Uri MatchUrl(this string value, out bool isMatch)
        {
            try
            {
                isMatch = true;
                return new Uri(value);
            }
            catch
            {
                isMatch = false;
                return null;
            }
        }
        /// <summary>
        /// 匹配完整格式的URL
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <returns>是否匹配成功</returns>
        public static bool MatchUrl(this string value)
        {
            MatchUrl(value, out var isMatch);
            return isMatch;
        }
        #endregion

        #region 权威校验身份证号码
        /// <summary>
        /// 根据GB11643-1999标准权威校验中国身份证号码的合法性
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <returns>是否匹配成功</returns>
        public static bool MatchIdentifyCard(this string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            string address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91";
            if (value.Length == 18)
            {
                if (long.TryParse(value.Remove(17), out var n) == false || n < Math.Pow(10, 16) || long.TryParse(value.Replace('x', '0').Replace('X', '0'), out n) == false)
                {
                    return false; //数字验证  
                }

                if (!address.Contains(value.Remove(2), StringComparison.Ordinal))
                {
                    return false; //省份验证  
                }

                string birth = value.Substring(6, 8).Insert(6, "-").Insert(4, "-");
                if (!DateTime.TryParse(birth, out _))
                {
                    return false; //生日验证  
                }

                string[] arrVarifyCode = "1,0,X,9,8,7,6,5,4,3,2".Split(',');
                string[] wi = "7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2".Split(',');
                char[] ai = value.Remove(17).ToCharArray();
                int sum = 0;
                for (int i = 0; i < 17; i++)
                {
                    sum += Convert.ToInt32(wi[i], CultureInfo.InvariantCulture) * Convert.ToInt32(ai[i], CultureInfo.InvariantCulture);
                }

                Math.DivRem(sum, 11, out var y);
                return arrVarifyCode[y] == value[^1..].ToUpperInvariant();
            }

            if (value.Length == 15)
            {
                if (long.TryParse(value, out var n) == false || n < Math.Pow(10, 14))
                {
                    return false; //数字验证  
                }

                if (!address.Contains(value.Remove(2), StringComparison.Ordinal))
                {
                    return false; //省份验证  
                }

                string birth = value.Substring(6, 6).Insert(4, "-").Insert(2, "-");
                return DateTime.TryParse(birth, out _);
            }

            return false;
        }
        #endregion

        #region 校验IP地址的合法性
        /// <summary>
        /// 校验IP地址的正确性，同时支持IPv4和IPv6
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <param name="isMatch">是否匹配成功，若返回true，则会得到一个Match对象，否则为null</param>
        /// <returns>匹配对象</returns>
        public static IPAddress MatchInetAddress(this string value, out bool isMatch)
        {
            isMatch = IPAddress.TryParse(value, out var ip);
            return ip;
        }
        /// <summary>
        /// 校验IP地址的正确性，同时支持IPv4和IPv6
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <returns>是否匹配成功</returns>
        public static bool MatchInetAddress(this string value)
        {
            MatchInetAddress(value, out var success);
            return success;
        }
        #endregion

        #region 校验手机号码的正确性
        /// <summary>
        /// 匹配手机号码
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <param name="isMatch">是否匹配成功，若返回true，则会得到一个Match对象，否则为null</param>
        /// <returns>匹配对象</returns>
        public static Match MatchPhoneNumber(this string value, out bool isMatch)
        {
            if (string.IsNullOrEmpty(value))
            {
                isMatch = false;
                return null;
            }

            Match match = Regex.Match(value, @"^((1[3,5,6,8][0-9])|(14[5,7])|(17[0,1,3,6,7,8])|(19[8,9]))\d{8}$");
            isMatch = match.Success;
            return isMatch ? match : null;
        }
        /// <summary>
        /// 匹配手机号码
        /// </summary>
        /// <param name="value">源字符串</param>
        /// <returns>是否匹配成功</returns>
        public static bool MatchPhoneNumber(this string value)
        {
            MatchPhoneNumber(value, out bool success);
            return success;
        }
        #endregion

        /// <summary>
        /// 字符串掩码
        /// </summary>
        /// <param name="value">字符串</param>
        /// <param name="mask">掩码符</param>
        /// <returns></returns>
        public static string Mask(this string value, char mask = '*')
        {
            if (string.IsNullOrWhiteSpace(value?.Trim()))
            {
                return value;
            }

            value = value.Trim();
            string masks = mask.ToString().PadLeft(4, mask);
            return value.Length switch
            {
                _ when value.Length >= 11 => Regex.Replace(value, @"(\w{3})\w*(\w{4})", $"$1{masks}$2"),
                _ when value.Length == 10 => Regex.Replace(value, @"(\w{3})\w*(\w{3})", $"$1{masks}$2"),
                _ when value.Length == 9 => Regex.Replace(value, @"(\w{2})\w*(\w{3})", $"$1{masks}$2"),
                _ when value.Length == 8 => Regex.Replace(value, @"(\w{2})\w*(\w{2})", $"$1{masks}$2"),
                _ when value.Length == 7 => Regex.Replace(value, @"(\w{1})\w*(\w{2})", $"$1{masks}$2"),
                _ when value.Length >= 2 && value.Length < 7 => Regex.Replace(value, @"(\w{1})\w*(\w{1})", $"$1{masks}$2"),
                _ => value + masks
            };
        }
        /// <summary>
        /// 邮箱掩码
        /// </summary>
        /// <param name="value">邮箱</param>
        /// <param name="mask">掩码</param>
        /// <returns></returns>
        public static string MaskEmail(this string value, char mask = '*')
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            var index = value.LastIndexOf("@", StringComparison.OrdinalIgnoreCase);
            var subString = value[0..index];
            return !MatchEmail(value) ? value : value.Replace(subString, Mask(subString, mask), StringComparison.CurrentCulture);
        }
    }
}
